#include "Wt.h"

/* include command */

int WtIncludeCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret;
  WtContext *w = Tcl_GetAssocData(interp, "wt::context", NULL);

  if (objc != 2) {
    Tcl_AppendResult(interp, "Incorrect usage. Should be: ",
      Tcl_GetString(objv[0]), " ?path?", NULL);
  } else {
    if (WtFindAndExecuteFile(w, objv[1])) {
      ret = TCL_OK;
    }
  }

  return ret;
}

/* serverVars command */

int WtServerVarsCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret;
  WtContext *w = Tcl_GetAssocData(interp, "wt::context", NULL);

  if (!w->active->request.envInitialized) {
    ap_add_common_vars(w->active->request.apReq);
    ap_add_cgi_vars(w->active->request.apReq);
    WtUpdateTableFromApTable(w->active->request.serverVars, w->active->request.apReq->subprocess_env);
    w->active->request.envInitialized = 1;
  }

  WtTblMethodHelper(clientData, interp, objc, objv, &ret,
    w->active->request.serverVars, 0, 1);

  return ret;
}

/* queryParams command */

int WtQueryParamsCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret;
  WtContext *w = Tcl_GetAssocData(interp, "wt::context", NULL);

  if (!WtCmdInitRequest(interp)) {
    return TCL_ERROR;
  }

  WtTblMethodHelper(clientData, interp, objc, objv, &ret,
    w->active->request.queryParams, 0, 1);

  return ret;
}

/* postParams command */

int WtPostParamsCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret;
  WtContext *w = Tcl_GetAssocData(interp, "wt::context", NULL);

  if (!WtCmdInitRequest(interp)) {
    return TCL_ERROR;
  }

  WtTblMethodHelper(clientData, interp, objc, objv, &ret,
    w->active->request.postParams, 0, 1);

  return ret;
}

/* params command */

int WtParamsCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret;
  WtContext *w = Tcl_GetAssocData(interp, "wt::context", NULL);

  if (!WtCmdInitRequest(interp)) {
    return TCL_ERROR;
  }

  /* ENH: Add separate init for combined params set */

  WtTblMethodHelper(clientData, interp, objc, objv, &ret,
    w->active->request.params, 0, 1);

  return ret;
}

/* requestCookies command */

int WtRequestCookiesCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret;
  WtContext *w = Tcl_GetAssocData(interp, "wt::context", NULL);

  if (!WtCmdInitRequest(interp)) {
    return TCL_ERROR;
  }

  WtTblMethodHelper(clientData, interp, objc, objv, &ret,
    w->active->request.cookies, 0, 1);

  return ret;
}

/* requestHeaders command */

int WtRequestHeadersCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret;
  WtContext *w = Tcl_GetAssocData(interp, "wt::context", NULL);

  if (!WtCmdInitRequest(interp)) {
    return TCL_ERROR;
  }

  WtTblMethodHelper(clientData, interp, objc, objv, &ret,
    w->active->request.headers, 0, 1);

  return ret;
}

/* responseHeaders command */

int WtResponseHeadersCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret;
  WtContext *w = Tcl_GetAssocData(interp, "wt::context", NULL);

  int status = WtTblMethodHelper(clientData, interp, objc,
    objv, &ret, w->active->response.headers, 0, 1);

  return ret;
}

/* successHeaders command */

int WtSuccessHeadersCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret;
  WtContext *w = Tcl_GetAssocData(interp, "wt::context", NULL);

  int status = WtTblMethodHelper(clientData, interp, objc,
    objv, &ret, w->active->response.successHeaders, 0, 0);

  return ret;
}

/* status command */

int WtStatusCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR, status;
  WtContext *w = Tcl_GetAssocData(interp, "wt::context", NULL);

  if (objc != 2 && objc != 3) {
    Tcl_AppendResult(interp, "Incorrect usage. Should be: ",
      Tcl_GetString(objv[0]), " ?status?", NULL);
  } else {
    if (objc == 2) {
      if (Tcl_GetIntFromObj(interp, objv[1], &status) == TCL_OK) {
        w->active->response.status = status;
        Tcl_SetObjResult(interp, Tcl_NewIntObj(w->active->response.status));
        ret = TCL_OK;
      }
    } else if (objc == 3) {
      Tcl_SetObjResult(interp, Tcl_NewIntObj(w->active->response.status));
    }
  }

  return ret;
}

/* contextId command */

int WtContextIdCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  Tcl_SetObjResult(interp, Tcl_NewIntObj(WtGetContextId()));
  return TCL_OK;
}

/* htmlEncode command */

int WtHtmlEncodeCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;
  Tcl_Obj *interpRes = Tcl_GetObjResult(interp);
  WtContext *w = Tcl_GetAssocData(interp, "wt::context", NULL);

  if (objc != 2) {
    Tcl_AppendStringsToObj(interpRes, "Incorrect usage. Should be: ",
      WtToString(objv[0]), " string", NULL);
  } else {
    Tcl_SetObjResult(interp,
      WtNewString(WtSafeStr(ap_escape_html(w->active->request.apReq->pool,
        WtToString(objv[1])))));
    ret = TCL_OK;
  }

  return ret;
}

/* scripts command */

int WtScriptsCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;
  WtContext *w = Tcl_GetAssocData(interp, "wt::context", NULL);

  if (objc != 1) {
    Tcl_AppendResult(interp, "Incorrect usage. Should be: ",
                     WtToString(objv[0]), NULL);
  } else {
    Tcl_SetObjResult(interp, w->active->server.scriptStack);
    ret = TCL_OK;
  }

  return ret;
}

/* serverLog command */

int WtParseLogLevel(const char *levelStr)
{
  int level = -1;

  if (!strcmp(levelStr, "emerg")) {
    level = APLOG_EMERG;
  } else if (!strcmp(levelStr, "alert")) {
    level = APLOG_ALERT;
  } else if (!strcmp(levelStr, "crit")) {
    level = APLOG_CRIT;
  } else if (!strcmp(levelStr, "error")) {
    level = APLOG_ERR;
  } else if (!strcmp(levelStr, "warning")) {
    level = APLOG_WARNING;
  } else if (!strcmp(levelStr, "notice")) {
    level = APLOG_NOTICE;
  } else if (!strcmp(levelStr, "info")) {
    level = APLOG_INFO;
  } else if (!strcmp(levelStr, "debug")) {
    level = APLOG_DEBUG;
  }

  return level;
}

int WtServerLogCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR, level, usage = 0;
  Tcl_Obj *interpRes = Tcl_GetObjResult(interp);
  WtContext *w = Tcl_GetAssocData(interp, "wt::context", NULL);
  char *path;

  if (objc < 2) {
    Tcl_AppendStringsToObj(interpRes, "Incorrect usage. Should be: ",
      NULL);
    usage = 1;
  } else {
    path = w->active->request.apReq->filename ? w->active->request.apReq->filename : "";

    if (objc == 2) {
      WtApLog(path, -1, APLOG_NOERRNO|APLOG_INFO, w,
        "Wtcl: %s", WtToString(objv[1]));
      ret = TCL_OK;
    } else if (objc == 3) {
      level = WtParseLogLevel(WtToString(objv[1]));
      if (level == -1) {
        Tcl_AppendStringsToObj(interpRes,
          "Unknown log level specified. Usage: ", NULL);
        usage = 1;
      } else {
        WtApLog(path, -1, APLOG_NOERRNO|level, w,
          "Wtcl: %s", WtToString(objv[2]));
        ret = TCL_OK;
      }
    }
  }

  if (usage) {
    Tcl_AppendStringsToObj(interpRes, WtToString(objv[0]),
      " [emerg | alert | crit | error | warning | notice | info | ",
      "debug | none] message", NULL);
  }

  return ret;
}

/* virtual command */

int WtVirtualCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret;
  WtContext *w = Tcl_GetAssocData(interp, "wt::context", NULL);
  request_rec *rr = NULL;
  const char *uri;

  if (objc != 2) {
    Tcl_AppendResult(interp, "Incorrect usage. Should be: ",
      Tcl_GetString(objv[0]), " ?path?", NULL);
  } else {
    uri = Tcl_GetString(objv[1]);
    rr = sub_req_lookup_uri(uri, w->active->request.apReq);
    if (!rr) {
      Tcl_AppendResult(interp, "Failed to initialize URI \"",
        uri, "\".");
    } else if (rr->status != 200) {
      statusObj = Tcl_NewIntObj(rr->status);
      Tcl_IncrRefCount(statusObj);
      Tcl_AppendResult(interp, "Failed to find URI \"", uri, "\": Status code: ",
        Tcl_GetString(statusObj), ".");          
      Tcl_DecrRefCount(statusObj);
    } else if (run_sub_req(rr)) {
      statusObj = Tcl_NewIntObj(rr->status);
      Tcl_IncrRefCount(statusObj);
      Tcl_AppendResult(interp, "Failed to run URI \"", uri, "\": Status code: ",
        Tcl_GetString(statusObj), ".");
      Tcl_DecrRefCount(statusObj);
    } else {
      ret = TCL_OK;
    }
  }

  if (rr) {
    destroy_sub_req(rr);
  }

  return ret;
}

/* type command */

int WtTypeCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;
  Tcl_Obj *interpRes = Tcl_GetObjResult(interp);
  WtContext *w = Tcl_GetAssocData(interp, "wt::context", NULL);

  if (objc != 2) {
    Tcl_AppendStringsToObj(interpRes, "Incorrect usage. Should be: ",
      WtToString(objv[0]), " value", NULL);
  } else {
    Tcl_SetObjResult(interp, WtNewString(
      WtSafeStr(objv[1]->typePtr->name)));
    ret = TCL_OK;
  }

  return ret;
}

/* objectInfo command */

int WtObjectInfoCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;
  Tcl_Obj *interpRes = Tcl_GetObjResult(interp);
  WtContext *w = Tcl_GetAssocData(interp, "wt::context", NULL);
  Tcl_Obj *obj, *list;

  if (objc != 2) {
    Tcl_AppendStringsToObj(interpRes, "Incorrect usage. Should be: ",
      WtToString(objv[0]), " object", NULL);
  } else {
    obj = objv[1];

    list = Tcl_NewListObj(0, NULL);

    Tcl_ListObjAppendElement(interp, list, WtNewString("address"));

    Tcl_ListObjAppendElement(interp, list, Tcl_NewIntObj((int)obj));

    Tcl_ListObjAppendElement(interp, list, WtNewString("type"));

    if (obj->typePtr && obj->typePtr->name) {
      Tcl_ListObjAppendElement(interp, list, WtNewString(obj->typePtr->name));
    } else {
      Tcl_ListObjAppendElement(interp, list, WtNewString(""));
    }

    Tcl_ListObjAppendElement(interp, list, WtNewString("hasValidString"));

    Tcl_ListObjAppendElement(interp, list, Tcl_NewBooleanObj(obj->bytes != NULL));

    Tcl_ListObjAppendElement(interp, list, WtNewString("bytes"));

    Tcl_ListObjAppendElement(interp, list, WtNewString(WtSafeStr(obj->bytes)));

    Tcl_ListObjAppendElement(interp, list, WtNewString("length"));

    Tcl_ListObjAppendElement(interp, list, Tcl_NewIntObj(obj->length));

    Tcl_ListObjAppendElement(interp, list, WtNewString("string"));

    Tcl_ListObjAppendElement(interp, list,
      WtNewString(WtSafeStr(WtToString(obj))));

    Tcl_SetObjResult(interp, list);

    ret = TCL_OK;
  }

  return ret;
}

/* winDebug command */

int WtWinDebugCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;
  Tcl_Obj *interpRes = Tcl_GetObjResult(interp);

  if (objc != 1) {
    Tcl_AppendStringsToObj(interpRes, "Incorrect usage. Should be: ",
      WtToString(objv[0]), NULL);
  } else {
    _asm int 3;
    ret = TCL_OK;
  }

  return ret;
}

/* Initialize commands. */

void WtInitBasicCommands(Tcl_Interp *interp)
{
  WtContext *w = Tcl_GetAssocData(interp, "wt::context", NULL);

  WtApLog(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, w,
    "Wtcl: WtInitBasicCommands.");

  Tcl_CreateObjCommand(interp, "::wt::include",
    WtIncludeCmd, NULL, NULL);

  Tcl_CreateObjCommand(interp, "::wt::serverVars",
    WtServerVarsCmd, NULL, NULL);

  Tcl_CreateObjCommand(interp, "::wt::queryParams",
    WtQueryParamsCmd, NULL, NULL);

  Tcl_CreateObjCommand(interp, "::wt::postParams",
    WtPostParamsCmd, NULL, NULL);

  Tcl_CreateObjCommand(interp, "::wt::params",
    WtParamsCmd, NULL, NULL);

  Tcl_CreateObjCommand(interp, "::wt::requestCookies",
    WtRequestCookiesCmd, NULL, NULL);

  Tcl_CreateObjCommand(interp, "::wt::requestHeaders",
    WtRequestHeadersCmd, NULL, NULL);

  Tcl_CreateObjCommand(interp, "::wt::responseHeaders",
    WtResponseHeadersCmd, NULL, NULL);

  Tcl_CreateObjCommand(interp, "::wt::successHeaders",
    WtSuccessHeadersCmd, NULL, NULL);

  Tcl_CreateObjCommand(interp, "::wt::status",
    WtStatusCmd, NULL, NULL);

  Tcl_CreateObjCommand(interp, "::wt::session::session",
                       WtSessionCmd, NULL, NULL);

  Tcl_CreateObjCommand(interp, "::wt::htmlEncode",
    WtHtmlEncodeCmd, NULL, NULL);

  Tcl_CreateObjCommand(interp, "::wt::virtual",
    WtVirtualCmd, NULL, NULL);

  Tcl_CreateObjCommand(interp, "::wt::scripts",
    WtScriptsCmd, NULL, NULL);

  Tcl_CreateObjCommand(interp, "::wt::serverLog",
    WtServerLogCmd, NULL, NULL);

  Tcl_CreateObjCommand(interp, "::wt::contextId",
    WtContextIdCmd, NULL, NULL);

  /* Debugging commands */

  Tcl_CreateObjCommand(interp, "type", WtTypeCmd,
    NULL, NULL);

  Tcl_CreateObjCommand(interp, "objectInfo", WtObjectInfoCmd,
    NULL, NULL);

  Tcl_CreateObjCommand(interp, "winDebug", WtWinDebugCmd,
    NULL, NULL);
}
